﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using mozhi.smarterp.AuthorCenter.Enums;
using mozhi.smarterp.AuthorCenter.OpenIddict.Repositories;
using mozhi.smarterp.AuthorCenter.Roles.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Clients;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;

namespace mozhi.smarterp.AuthorCenter.Roles
{
    [AllowAnonymous]
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IIdentityRoleAppService), typeof(IdentityRoleAppService), typeof(ICustomIdentityRoleAppService), typeof(CustomIdentityRoleAppService))]
    public class CustomIdentityRoleAppService : IdentityRoleAppService, ICustomIdentityRoleAppService
    {
        private ICurrentClient _currentClient;
        private IOpenIddictClientTypeRepository _openIddictUserTypeRepository;
        private ICustomIdentityRoleRepository _roleRepository;

        public CustomIdentityRoleAppService(IdentityRoleManager roleManager, ICustomIdentityRoleRepository roleRepository ,
            ICurrentClient currentClient , IOpenIddictClientTypeRepository openIddictClientTypeRepository) 
            : base(roleManager, roleRepository)
        {
            _currentClient = currentClient;
            _openIddictUserTypeRepository = openIddictClientTypeRepository;
            _roleRepository = roleRepository;
        }

        [AllowAnonymous]
        public override Task<PagedResultDto<IdentityRoleDto>> GetListAsync(GetIdentityRolesInput input)
        {
            return base.GetListAsync(input);
        }

        [AllowAnonymous]
        public override async Task<IdentityRoleDto> GetAsync(Guid id)
        {
            return await base.GetAsync(id);
        }

        [AllowAnonymous]
        public override async Task<IdentityRoleDto> CreateAsync(IdentityRoleCreateDto input)
        {
            var clientId = _currentClient.Id;

            var clientType = (await _openIddictUserTypeRepository.GetListAsync(null, int.MaxValue, 0, clientId)).FirstOrDefault();

            var roleTypes = GetRoleTypesByClientType(clientType?.ClientType);

            if (!roleTypes.Contains((RoleType)input.GetProperty<int>("RoleType")))
                throw new UserFriendlyException($"不能创建类型为：{input.GetProperty<int>("RoleType")}的角色");

            IdentityRole role = new IdentityRole(base.GuidGenerator.Create(), input.Name, base.CurrentTenant.Id)
            {
                IsDefault = input.IsDefault,
                IsPublic = input.IsPublic
            };
            input.MapExtraPropertiesTo(role);
            (await RoleManager.CreateAsync(role).ConfigureAwait(continueOnCapturedContext: false)).CheckErrors();
            await base.CurrentUnitOfWork.SaveChangesAsync().ConfigureAwait(continueOnCapturedContext: false);
            return base.ObjectMapper.Map<IdentityRole, IdentityRoleDto>(role);
        }

        [AllowAnonymous]
        public override async Task<IdentityRoleDto> UpdateAsync(Guid id, IdentityRoleUpdateDto input)
        {
            var clientId = _currentClient.Id;

            var clientType = (await _openIddictUserTypeRepository.GetListAsync(null, int.MaxValue, 0, clientId)).FirstOrDefault();

            var roleTypes = GetRoleTypesByClientType(clientType?.ClientType);

            if (!roleTypes.Contains(input.GetProperty<RoleType>("RoleType")))
                throw new UserFriendlyException($"不能修改类型为：{input.GetProperty<int>("RoleType")}的角色");

            IdentityRole role = await RoleManager.GetByIdAsync(id).ConfigureAwait(continueOnCapturedContext: false);
            role.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
            (await RoleManager.SetRoleNameAsync(role, input.Name).ConfigureAwait(continueOnCapturedContext: false)).CheckErrors();
            role.IsDefault = input.IsDefault;
            role.IsPublic = input.IsPublic;
            input.MapExtraPropertiesTo(role);
            (await RoleManager.UpdateAsync(role).ConfigureAwait(continueOnCapturedContext: false)).CheckErrors();
            await base.CurrentUnitOfWork.SaveChangesAsync().ConfigureAwait(continueOnCapturedContext: false);
            return base.ObjectMapper.Map<IdentityRole, IdentityRoleDto>(role);
        }

        [AllowAnonymous]
        public async Task<PagedResultDto<IdentityRoleDto>> GetListAsync(IdentityRoleFilter filter)
        {
            var clientId = _currentClient.Id;

            var clientType = (await _openIddictUserTypeRepository.GetListAsync(null, int.MaxValue, 0, clientId)).FirstOrDefault();

            var roleTypes = GetRoleTypesByClientType(clientType?.ClientType);

            if (roleTypes.Count == 0)
                return new PagedResultDto<IdentityRoleDto>();

            if (filter.RoleType.HasValue && !roleTypes.Contains((RoleType)filter.RoleType.Value))
                return new PagedResultDto<IdentityRoleDto>();

            if (filter.RoleType.HasValue)
                roleTypes = new List<RoleType>() { (RoleType)filter.RoleType.Value };

            long count = await _roleRepository.GetCountAsync(filter.Filter, roleTypes).ConfigureAwait(continueOnCapturedContext: false);
            List<IdentityRole> source = await _roleRepository.GetListAsync(filter.Sorting, filter.MaxResultCount, filter.SkipCount, filter.Filter, false, roleTypes)
                .ConfigureAwait(continueOnCapturedContext: false);
            return new PagedResultDto<IdentityRoleDto>(count, base.ObjectMapper.Map<List<IdentityRole>, List<IdentityRoleDto>>(source));
        }

        [AllowAnonymous]
        public override async Task<ListResultDto<IdentityRoleDto>> GetAllListAsync()
        {
            var clientId = _currentClient.Id;

            var clientType = (await _openIddictUserTypeRepository.GetListAsync(null, int.MaxValue, 0, clientId)).FirstOrDefault();

            var roleTypes = GetRoleTypesByClientType(clientType?.ClientType);

            if (roleTypes.Count == 0)
                return new ListResultDto<IdentityRoleDto>();

            var roles = await base.GetAllListAsync();

            if (roles.Items == null || roles.Items.Count == 0)
                return roles;

            roles.Items = roles.Items.Where(x => x.ExtraProperties["RoleType"] != null && roleTypes.Contains((RoleType)x.ExtraProperties["RoleType"])).ToList();

            return roles;
        }

        #region
        private List<RoleType> GetRoleTypesByClientType(ClientType? clientType)
        {
            if (clientType == ClientType.PlatformClient)
                return new List<RoleType>() { RoleType.PlatformRole , RoleType.EnterpriseRole };

            if (clientType == ClientType.EnterpriseClient)
                return new List<RoleType>() { RoleType.EnterpriseRole};

            return new List<RoleType>();
        }
        #endregion
    }
}
